﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.AspNetCore.Http;

namespace net.xBei.WebApi.Helper
{
    /// <summary>
    /// 网络帮助类
    /// </summary>
    public static class NetHelper
    {
        private static readonly Regex regexIpV4 = new("^([12]{0,1}\\d{1,2}.){3}[12]{0,1}\\d{1,2}$", RegexOptions.Compiled);
        /// <summary>
        /// 
        /// </summary>
        /// <param name="httpRequest"></param>
        /// <returns></returns>
        public static IPAddress? GetIpAddress(this HttpRequest httpRequest) =>
                (httpRequest.Headers.TryGetValue("X-Real-IP", out var XRealIP)
                    ? XRealIP.Select(x => IPAddress.TryParse(x, out var tmp) ? tmp : null).FirstOrDefault()
                    : null
                )
                ?? (httpRequest.Headers.TryGetValue("X-Forwarded-For", out var XForwardedFor)
                    ? XForwardedFor.Select(x => IPAddress.TryParse(x, out var tmp) ? tmp : null).FirstOrDefault()
                    : null
                )
                ?? httpRequest.HttpContext.Connection.RemoteIpAddress;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="httpRequest"></param>
        /// <returns></returns>
        public static IPAddress? GetIpAddress2(this HttpRequest httpRequest) {
            var ip = httpRequest.Headers.TryGetValue("X-Real-IP", out var XRealIP)
                ? XRealIP.FirstOrDefault(x=> IPAddress.TryParse(x, out var _))?.Trim()
                : "";
            if (IPAddress.TryParse(ip, out var address)) {
                return address;
            }
            ip = httpRequest.Headers.TryGetValue("X-Forwarded-For", out var XForwardedFor)
                ? XForwardedFor.FirstOrDefault(x => IPAddress.TryParse(x, out var _))?.Trim()
                : "";
            return IPAddress.TryParse(ip, out address) ? address : httpRequest.HttpContext.Connection.RemoteIpAddress;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="str"></param>
        /// <param name="address"></param>
        /// <returns></returns>
        public static bool IsIpAddress(string str, out IPAddress? address)
            => IPAddress.TryParse(str, out address);
        /// <summary>
        /// IpV4
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static bool IsIpAddressV4(string str) =>
            IPAddress.TryParse(str.Trim(), out var address) && (address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork || address.IsIPv4MappedToIPv6);
        /// <summary>
        /// 是否IpV4
        /// </summary>
        /// <param name="str"></param>
        /// <param name="ipAddress"></param>
        /// <returns></returns>
        public static bool IsIpAddressV4(string str, out string ipAddress) {
            ipAddress = str;
            if (IPAddress.TryParse(str.Trim(), out var address)) {
                ipAddress = address.IsIPv4MappedToIPv6 ? address.MapToIPv4().ToString() : address.ToString();
                return true;
            }
            return false;
        }
        /// <summary>
        /// 判断IP地址是否相同
        /// </summary>
        /// <param name="ip1"></param>
        /// <param name="ip2"></param>
        /// <returns></returns>
        public static bool IpAddressEquals(string ip1, string ip2)
            => IsIpAddressV4(ip1) && Ip2Long(ip1) == Ip2Long(ip2);
        
        /// <summary>
        /// IpV4地址转长整型（LONG）
        /// </summary>
        /// <param name="ipAddress">IPV4地址</param>
        /// <returns>地址无效会返回0</returns>
        public static long Ip2Long(string ipAddress)
            => IsIpAddressV4(ipAddress)
                ? ipAddress.Split('.').Select((s, i) => long.Parse(s) << ((3 - i) * 8)).Sum()
                : 0;
        /// <summary>
        /// 长整型转IpV4
        /// </summary>
        /// <param name="ipValue"></param>
        /// <returns></returns>
        public static string Long2Ip(long ipValue)
            => string.Join('.', new[] { (byte)((ipValue & 0xFF000000) >> 24), (byte)((ipValue & 0xFF0000) >> 16), (byte)((ipValue & 0xFF00) >> 8), (byte)(ipValue & 0xFF) });
    }
}
